module ActiveRecord
  module ValidatesWordCount

    def self.included(base)
      base.extend(ClassMethods)
      setup_default_error_messages
    end
    
    def self.setup_default_error_messages
      ActiveRecord::Errors.default_error_messages[:should_not_exceed_word_count]  = "should not have more than %d words"
      ActiveRecord::Errors.default_error_messages[:should_exceed_word_count]      = "should have more than %d words"
      ActiveRecord::Errors.default_error_messages[:word_count_should_be_in_range] = "word count should be between %d and %d"
    end

    module ClassMethods
      
      def validates_word_count(*attr_names)

        configuration = { :message => '', :on => :save, :with => nil }
        configuration.update(attr_names.extract_options!)
        
        get_message = Proc.new do |key| 
          configuration[:message].empty? ? ActiveRecord::Errors.default_error_messages[key] : configuration[:message]
        end
        
        proc = nil
        
        if configuration[:maximum]
          configuration[:message] = get_message.call(:should_not_exceed_word_count) % configuration[:maximum].to_i
          proc = Proc.new { |words| words <= configuration[:maximum].to_i }
        elsif configuration[:minimum]
          configuration[:message] = get_message.call(:should_exceed_word_count) % configuration[:minimum].to_i
          proc = Proc.new { |words| words >= configuration[:minimum].to_i }
        elsif configuration[:in] && configuration[:in].is_a?(Range)
          configuration[:message] = get_message.call(:word_count_should_be_in_range) % [configuration[:in].begin, configuration[:in].end]
          range = configuration[:in]
          proc = Proc.new do |words| 
            range_end_condition = range.exclude_end? ?  (words < range.end) : (words <= range.end)
            (words >= range.begin && range_end_condition)
          end
        else
          raise(ArgumentError, ":maximum => int, :minimum => int, or :in => Range argument must be supplied!")
        end
        
        validates_each(attr_names, configuration) do |record, attr_name, value|
          next if configuration[:allow_nil] and value.nil?
          record.errors.add(attr_name, configuration[:message]) unless proc.call(value.to_s.words)
        end
        
      end
      
    end
    
  end
end

